<?php
class DbQueryResult_MySQL implements IDbQueryResult
{
    protected $selectAutoFree = true;
    protected $query;
    protected $conn;
    protected $result;
    protected $error;
    protected $row;
    protected $fields = array();
    protected $assoc  = null;

    public function __construct (IDbQuery $query, $conn) {
        $this->query  = $query;
        $this->conn   = $conn;
        $this->result = mysql_query($query->getQuery(), $conn);
        $this->error  = mysql_errno($conn) . ": " . mysql_error($conn) . ";";
        if ($query->getType() == 'select') {
            while ( $row = mysql_fetch_field($this->result) ) {
                $this->fields[] = $row;
            }
        }
    }

    /**
     * Определяет, есть ли следующая строка запроса и переходит на неё, либо возвращает false
     * Использование: while ($result->row()) { }
     * @return bool
     */
    public function row () {
        $this->assoc = null;
        $this->row = mysql_fetch_array($this->result);
        $row = !!$this->row;
        if (!$row && $this->selectAutoFree) {
            $this->free();
        }
        return $row;
    }

    /**
     * Освобождает память от результата выполнения запроса
     */
    public function free () {
        mysql_free_result($this->result);
    }

    protected function formAssoc () {
        if ($this->assoc) {
            return $this->assoc;
        }
        $rows = array();
        foreach ($this->fields as $num => $field) {
            // Для груповых полей, алля Max, Count
            $table = $field->table ? $field->table : 0;
            $rows[$table][$field->name] = $this->row[$num];
        }
        return ($this->assoc = $rows);
    }

    protected function collectFields ($name) {
        $rows = array();
        foreach ($this->fields as $num => $field) {
            // Для груповых полей, алля Max, Count
            $table = $field->table ? $field->table : 0;
            if ($field->name == $name) {
                $rows[$table] = $this->row[$num];
            }
        }
        return $rows;
    }

    protected function getField ($table, $field) {
        foreach ($this->fields as $num => $curField) {
            $curTable = $curField->table ? $curField->table : '';
            if ($table == $curTable && $curField->name == $field) {
                return $this->row[$num];
            }
        }
    }

    /**
     * Возвращает определённую строку, или массив, или двумерный массив результата из бд. Примеры:
     * "field"   — получить значение последнего поля с таким названием
     * "*.field" — возвращает одномерный массив, где ключём будет имя таблицы, а значением — значения поля field в соответствующей таблице
     * "table.*" — получить ассоциативный массив, где ключ — имя поля в таблице table
     * "*.*"     — получить массив, в котором ключи — имена таблиц, а значение — массивы, в которых ключи — имена полей
     * "*"       — получить массив, в котором ключи — имена полей
     * ".field"  — получить значение группового поля, например "COUNT(*)"
     * @param string $field
     * @return string|array
     */
    public function get ($field) {
        if ($field == '*.*') {
            return $this->formAssoc();
        } else if ($field == '*') {
            return $this->row;
        } else {
            $parts = explode('.', $field);
            if (isset($parts[1])) {
                $table = $parts[0];
                $field = $parts[1];
                if ($table == '*') {
                    return $this->collectFields($name);
                } else if ($field == '*') {
                    $assoc = $this->formAssoc();
                    return $assoc[$table ? $table : 0];
                } else {
                    return $this->getField($table, $field);
                }
            } else {
                return $this->row[$field];
            }
        }
    }

    /**
     * Возвращает autoincrement ID последней вставленной записи
     * @return int
     */
    public function getId () {
        return mysql_insert_id ($this->conn);
    }

    /**
     * Возвращает количество затронутых, или полученных select'ом строк
     * @return int
     */
    public function getNum () {
        return $this->query->getType() == 'select' ?
            mysql_num_rows($this->result) : mysql_affected_rows($this->conn);
    }

    /**
     * Получить ошибку, или null, если ошибок не было
     * @return string|null
     */
    public function getError () {
        return $this->error;
    }
}